package com.ylr.config.framework.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ylr.config.common.constant.ConfigConstant;
import com.ylr.config.common.vo.enum_.EnumFormVo;
import com.ylr.config.common.vo.enum_.EnumVo;
import com.ylr.config.framework.entity.EnumEntity;
import com.ylr.config.framework.mapper.EnumMapper;
import com.ylr.config.framework.service.EnumService;
import com.ylr.base.common.constant.RedisConstant;
import com.ylr.base.common.dto.NameValue;
import com.ylr.base.common.dto.PageDto;
import com.ylr.base.common.enums.CommonEnum;
import com.ylr.base.common.enums.ResultEnum;
import com.ylr.base.common.exception.YlrException;
import com.ylr.base.common.po.user.UserPo;
import com.ylr.base.common.utils.DataUtils;
import com.ylr.base.common.utils.RandomNumUtils;
import com.ylr.base.common.utils.ThrottleUtils;
import com.ylr.base.common.utils.UserUtils;
import com.ylr.base.framework.service.AsyncService;
import com.ylr.base.framework.service.SnowflakeService;
import com.ylr.config.common.dto.enum_.EnumDto;
import com.ylr.redis.framework.service.RedisService;
import com.ylr.redis.framework.service.RedisStringService;
import com.ylr.redis.framework.service.RedissonService;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wyw
 * date: 2023-05-02 18:59:16
 * className: EnumServiceImpl
 * version: 1.0
 * description:
 */
@Service("enumService")
@AllArgsConstructor
public class EnumServiceImpl extends ServiceImpl<EnumMapper, EnumEntity> implements EnumService {

    private static final Logger log = LoggerFactory.getLogger(EnumServiceImpl.class);

    private final SnowflakeService snowflakeService;

    private final AsyncService asyncService;

    private final RedisStringService redisStringService;

    private final RedisService redisService;

    private final RedissonService redissonService;

    private final ThrottleUtils throttleUtils;

    @Override
    public void saveEnum(EnumVo enumVo) {
        // 获取正在保存枚举配置的用户对象
        UserPo userPo = UserUtils.getUser();
        Date date = new Date();

        EnumEntity _enum = this.getEnum(enumVo, userPo, date);
        // 插入数据
        baseMapper.insert(_enum);
        log.info("枚举配置插入完成");

        // 删除缓存
        this.deleteCache(Collections.singletonList(_enum.getType()));
    }

    @Transactional
    @Override
    public void batchSaveEnum(List<EnumVo> enumVoList) {

        // 获取正在批量保存枚举配置的用户对象
        UserPo userPo = UserUtils.getUser();
        Date date = new Date();

        List<EnumEntity> enumList = new ArrayList<>(enumVoList.size());
        List<Integer> typeList = new ArrayList<>(enumVoList.size());
        for (EnumVo enumVo : enumVoList) {
            EnumEntity _enum = this.getEnum(enumVo, userPo, date);

            enumList.add(_enum);
            typeList.add(enumVo.getType());
        }
        // 批量插入数据
        this.saveBatch(enumList);
        log.info("批量插入枚举配置完成");

        // 删除缓存
        this.deleteCache(typeList);
    }

    @Override
    public PageDto<EnumDto> pageEnumList(EnumFormVo enumFormVo) {

        // mp分页对象
        Page<EnumEntity> page = new Page<>(enumFormVo.getPageNo(), enumFormVo.getPageSize());

        LambdaQueryWrapper<EnumEntity> enumLambdaQueryWrapper = new LambdaQueryWrapper<>();
        // 查询字段
        enumLambdaQueryWrapper.select(EnumEntity::getId, EnumEntity::getType, EnumEntity::getEnumName, EnumEntity::getEnumValue, EnumEntity::getSort,
                EnumEntity::getStatus, EnumEntity::getUpdateTime, EnumEntity::getUpdateUser, EnumEntity::getCreateTime, EnumEntity::getCreateUser);
        // 匹配条件
        enumLambdaQueryWrapper.eq(Objects.nonNull(enumFormVo.getType()), EnumEntity::getType, enumFormVo.getType())
                .like(StringUtils.isNotBlank(enumFormVo.getWord()), EnumEntity::getEnumName, enumFormVo.getWord())
                .eq(Objects.nonNull(enumFormVo.getStatus()), EnumEntity::getStatus, enumFormVo.getStatus())
                .eq(EnumEntity::getDeleteStatus, CommonEnum.DELETE_NO.getValue());
        // 分页查询
        baseMapper.selectPage(page, enumLambdaQueryWrapper);

        List<EnumEntity> enumList = page.getRecords();
        long totalNum = page.getTotal();

        List<EnumDto> enumDtoList = new ArrayList<>(enumList.size());
        for (EnumEntity _enum : enumList) {
            EnumDto enumDto = this.getEnumDto(_enum);

            enumDtoList.add(enumDto);
        }

        return new PageDto<>(enumDtoList, totalNum, enumFormVo.getPageNo(), enumFormVo.getPageSize());
    }

    @Override
    public List<NameValue<String>> getEnumList(Integer type) {

        String redisKey = ConfigConstant.ENUM_PREFIX + type;
        // 查询缓存
        List<NameValue<String>> cacheList = redisStringService.get(redisKey);
        if (Objects.nonNull(cacheList)) {
            Runnable runnable = () -> {
                // 尝试刷新键的过期时间
                redisService.tryUpdateKeyExpire(redisKey, 150, RandomNumUtils.random5Minutes(10));
            };
            // 限流执行
            throttleUtils.throttleRun(runnable, "getEnumList" + type, 1500, TimeUnit.MILLISECONDS);
            return cacheList;
        }

        String lockKey = RedisConstant.LOCK_PREFIX + redisKey;
        Supplier<List<NameValue<String>>> supplier = () -> {
            // double check
            List<NameValue<String>> list = redisStringService.get(redisKey);
            if (Objects.nonNull(list)) {
                return list;
            }

            LambdaQueryWrapper<EnumEntity> enumLambdaQueryWrapper = new LambdaQueryWrapper<>();
            // 查询字段
            enumLambdaQueryWrapper.select(EnumEntity::getEnumName, EnumEntity::getEnumValue);
            // 匹配条件
            enumLambdaQueryWrapper.eq(EnumEntity::getType, type)
                    .eq(EnumEntity::getDeleteStatus, CommonEnum.DELETE_NO.getValue());
            // 查询数据
            List<EnumEntity> enumList = baseMapper.selectList(enumLambdaQueryWrapper);

            // 数据封装
            list = new ArrayList<>(enumList.size());
            for (EnumEntity _enum : enumList) {
                list.add(new NameValue<>(_enum.getEnumName(), _enum.getEnumValue().toString()));
            }

            // 添加缓存
            redisStringService.set(redisKey, list, RandomNumUtils.random5Minutes(10), TimeUnit.SECONDS);

            return list;
        };
        // 加锁运行
        return redissonService.lockRun(lockKey, supplier);
    }

    @Override
    public List<Long> getValueList(Integer type) {
        String redisKey = ConfigConstant.ENUM_PREFIX + type + ConfigConstant.VALUE_LIST_SUFFIX;
        // 查询缓存
        List<Long> cacheEnumValueList = redisStringService.get(redisKey);
        if (Objects.nonNull(cacheEnumValueList)) {
            Runnable runnable = () -> {
                // 尝试刷新键的过期时间
                redisService.tryUpdateKeyExpire(redisKey, 150, RandomNumUtils.random5Minutes(10));
            };
            // 限流执行
            throttleUtils.throttleRun(runnable, "getValueList" + type, 1500, TimeUnit.MILLISECONDS);
            return cacheEnumValueList;
        }

        String lockKey = RedisConstant.LOCK_PREFIX + redisKey;
        Supplier<List<Long>> supplier = () -> {
            // double check
            List<Long> enumValueList = redisStringService.get(redisKey);
            if (Objects.nonNull(enumValueList)) {
                return enumValueList;
            }
            // 查询枚举配置值列表
            enumValueList = baseMapper.selectEnumValueList(type, CommonEnum.DELETE_NO.getValue());

            // 添加缓存
            redisStringService.set(redisKey, enumValueList, RandomNumUtils.random5Minutes(10), TimeUnit.SECONDS);

            return enumValueList;
        };
        // 加锁运行
        return redissonService.lockRun(lockKey, supplier);
    }

    @Override
    public EnumDto getEnum(Long id) {
        // 查询数据
        EnumEntity _enum = baseMapper.selectById(id);
        if (CommonEnum.DELETE_YES.getValue().equals(_enum.getDeleteStatus())) {
            throw new YlrException(ResultEnum.NOT_FOUND);
        }

        return this.getEnumDto(_enum);
    }

    @Override
    public void updateEnum(EnumVo enumVo) {
        // 获取正在修改枚举配置的用户对象
        UserPo userPo = UserUtils.getUser();
        Date date = new Date();

        EnumEntity _enum = new EnumEntity();
        _enum.setId(enumVo.getId())
            .setType(enumVo.getType())
            .setEnumName(enumVo.getEnumName())
            .setEnumValue(enumVo.getEnumValue())
            .setSort(enumVo.getSort())
            .setUpdateTime(date)
            .setUpdateUser(userPo.getUsername())
            .setRemark(enumVo.getRemark());
        // 插入数据
        int count = baseMapper.updateById(_enum);
        log.info("枚举配置修改完成。count={}", count);
        if (count == 0) {
            throw new YlrException(ResultEnum.NOT_FOUND);
        }

        // 删除缓存
        this.deleteCache(Collections.singletonList(_enum.getType()));
    }

    @Override
    public void deleteEnum(List<Long> idList) {

        // 查询主键列表关联的所有枚举类型
        Set<Integer> typeSet = baseMapper.selectTypeList(idList, CommonEnum.DELETE_NO.getValue());
        if (typeSet.isEmpty()) {
            log.info("主键列表匹配到枚举类型集合为空，数据不存在");
            throw new YlrException(ResultEnum.NOT_FOUND);
        }

        // 获取正在删除枚举配置的用户对象
        UserPo userPo = UserUtils.getUser();
        Date date = new Date();

        LambdaUpdateWrapper<EnumEntity> enumLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        // 设置字段值
        enumLambdaUpdateWrapper.set(EnumEntity::getDeleteStatus, CommonEnum.DELETE_YES.getValue())
                .set(EnumEntity::getUpdateTime, date)
                .set(EnumEntity::getUpdateUser, userPo.getUsername())
                .set(EnumEntity::getUpdateUserId, userPo.getId());
        // 匹配条件
        enumLambdaUpdateWrapper.in(EnumEntity::getId, idList)
                .eq(EnumEntity::getDeleteStatus, CommonEnum.DELETE_NO.getValue());
        // 更新数据
        int count = baseMapper.update(null, enumLambdaUpdateWrapper);
        log.info("枚举配置删除完成。count={}", count);
        if (count == 0) {
            throw new YlrException(ResultEnum.NOT_FOUND);
        }

        // 删除缓存
        this.deleteCache(typeSet);
    }

    /**
     * 获取枚举配置对象
     * @param enumVo 参数对象
     * @param userPo 操作用户对象
     * @param date   日期对象
     * @return 枚举配置对象
     */
    private EnumEntity getEnum(EnumVo enumVo, UserPo userPo, Date date) {
        EnumEntity _enum = new EnumEntity();
        _enum.setId(snowflakeService.getId())
                .setType(enumVo.getType())
                .setEnumName(enumVo.getEnumName())
                .setEnumValue(enumVo.getEnumValue())
                .setSort(enumVo.getSort())
                .setStatus(CommonEnum.STATUS_USE.getValue())
                .setDeleteStatus(CommonEnum.DELETE_NO.getValue())
                .setUpdateTime(date)
                .setUpdateUser(StringUtils.EMPTY)
                .setUpdateUserId(CommonEnum.DEFAULT_ID.getLongValue())
                .setCreateTime(date)
                .setCreateUser(userPo.getUsername())
                .setCreateUserId(userPo.getId())
                .setRemark(Optional.ofNullable(enumVo.getRemark()).orElse(StringUtils.EMPTY));
        return _enum;
    }

    /**
     * 获取枚举配置对象
     * @param _enum 参数对象
     * @return 枚举配置对象
     */
    private EnumDto getEnumDto(EnumEntity _enum) {
        EnumDto enumDto = new EnumDto();
        enumDto.setId(_enum.getId().toString())
                .setType(_enum.getType())
                .setEnumName(_enum.getEnumName())
                .setEnumValue(_enum.getEnumValue().toString())
                .setSort(_enum.getSort())
                .setStatus(_enum.getStatus())
                .setUpdateTime(_enum.getUpdateTime())
                .setUpdateUser(_enum.getUpdateUser())
                .setCreateTime(_enum.getCreateTime())
                .setCreateUser(_enum.getCreateUser())
                .setRemark(_enum.getRemark());
        return enumDto;
    }

    /**
     * 删除缓存
     * 根据枚举类型，删除对应缓存数据
     * @param typeList 枚举类型数组
     */
    private void deleteCache(Collection<Integer> typeList) {
        List<String> redisKeyList = new ArrayList<>(typeList.size());
        StringBuilder sb = new StringBuilder(ConfigConstant.ENUM_PREFIX);
        int length = ConfigConstant.ENUM_PREFIX.length();
        for (Integer type : typeList) {
            // 替换字符串
            DataUtils.replaceStringBuilder(sb, length, String.valueOf(type));

            redisKeyList.add(sb.toString());
        }
        // 删除缓存
        redisService.delete(redisKeyList);
        asyncService.schedule(() -> {
            // 延迟双删
            redisService.delete(redisKeyList);
        }, 1500, TimeUnit.MILLISECONDS);
    }
}